type Coord = (usize, usize);

#[derive(Debug, Clone, Copy)]
enum Direction {
    Left,
    Right,
}

const CHAMBER_WIDTH: usize = 7;

fn five_rock_coords() -> [Vec<Coord>; 5] {
    [
        vec![(0, 0), (1, 0), (2, 0), (3, 0)],
        vec![(1, 0), (0, 1), (1, 1), (2, 1), (1, 2)],
        vec![(0, 0), (1, 0), (2, 0), (2, 1), (2, 2)],
        vec![(0, 0), (0, 1), (0, 2), (0, 3)],
        vec![(0, 0), (0, 1), (1, 0), (1, 1)]
    ]
}

fn parse_input() -> Vec<Direction> {
    include_str!("../input.txt")
        .chars()
        .map(|c| -> Direction {
            match c {
                '<' => Direction::Left,
                '>' => Direction::Right,
                _ => panic!("Bad input")
            }
        })
        .collect()
}

fn absolute_coords(coords: &[Coord], bias_coord: Coord) -> Vec<Coord> {
    coords.into_iter()
        .map(|&(x, y)| (x + bias_coord.0, y + bias_coord.1))
        .collect()
}

fn part1() {
    let mut array: Vec<[bool; CHAMBER_WIDTH]> = vec![[true; CHAMBER_WIDTH]];

    let dirs = parse_input();
    let mut dir_iter = dirs.into_iter().cycle();

    for (_, rock_coords) in Iterator::zip((0..2022).into_iter(), five_rock_coords().into_iter().cycle()) {
        let mut bias_coord = (2, array.len() + 3);

        // Rocks move
        loop {
            let abs_coords = absolute_coords(&rock_coords, bias_coord);

            // Gas push!
            match dir_iter.next().unwrap() {
                Direction::Left => {
                    if abs_coords.iter()
                        .all(|&(x, y)| -> bool {
                            if x == 0 {
                                return false;
                            }
                            
                            if y < array.len() {
                                if array[y][x-1] {
                                    return false;
                                }
                            }

                            true
                        })
                    {
                        bias_coord.0 -= 1;
                    }

                }
                Direction::Right => {
                    if abs_coords.iter()
                        .all(|&(x, y)| -> bool {
                            if x == CHAMBER_WIDTH - 1 {
                                return false;
                            }
                            
                            if y < array.len() {
                                if array[y][x+1] {
                                    return false;
                                }
                            }

                            true
                        })
                    {
                        bias_coord.0 += 1;
                    }
                }
            }

            // Fall down
            let abs_coords = absolute_coords(&rock_coords, bias_coord);
            if abs_coords.iter()
                .all(|&(x, y)| -> bool {
                    if y - 1 < array.len() {
                        if array[y-1][x] {
                            return false;
                        }
                    }
                    true
                })
            {
                bias_coord.1 -= 1;
            } else {
                let current_layer_count = array.len();
                let next_layer_count = abs_coords.iter().map(|&(_, y)| y).max().unwrap() + 1;
                if next_layer_count > current_layer_count {
                    for _ in 0..(next_layer_count - current_layer_count) {
                        array.push([false; CHAMBER_WIDTH]);
                    }
                }

                abs_coords.iter()
                    .for_each(|&(x, y)| {
                        array[y][x] = true;
                    });

                break;
            }
        }
    }   

    println!("Part 1: {}", array.len() - 1); 
}

fn part2() {
    println!("{}", parse_input().len())
}

fn main() {
    part1();
    part2();
}
